/**
 *	@file	fld_mushitori.c
 *	@brief	虫取り大会専用フィールドオーバーレイソース(field_tradeとかと平行)
 *	@author	Miyuki Iwasawa
 *	@date	09.03.04
 */

#include "common.h"

#include "fieldsys.h"

#include "system/mystatus.h"
#include "system/wordset.h"
#include "poketool/poke_tool.h"

#include "field_subproc.h"
#include "field/field.h"
#include "field/situation_local.h"
#include "field/script.h"
#include "field/syswork.h"
#include "field/fld_bgm.h"
#include "fieldmap.h"
#include "map_attr.h"
#include "zonedata.h"
#include "map_tool.h"
#include "scr_tool.h"
#include "itemtool/item.h"
#include "battle/trtype_def.h"
#include "ev_time.h"
#include "fielddata/maptable/zone_id.h"
#include "fld_mushitori.h"
#include "msgdata/msg_mushitori.h"

#define MUSHITORI_BALL_NUM	(20)	//ボールの初期数
#define MY_ENTRY_ID	(5)

#define MUSHI_TRAINER_DAT_FILE	"data/mushi/mushi_trainer.bin"
#define MUSHI_ENCOUNT_DAT_FILE	"data/mushi/mushi_encount.bin"


static void sys_PokePartyPush(FLD_MUSHITORI_WORK* wk);
static void sys_PokePartyPop(FLD_MUSHITORI_WORK* wk);
static void sys_EntryCheck(FLD_MUSHITORI_WORK* wk);
static void sys_GetEncountTbl(FLD_MUSHITORI_WORK* wk);
static u16 sys_CalcPoint(FLD_MUSHITORI_WORK* wk,POKEMON_PARAM* pp);

/**
 *	@brief	虫取り大会システムワーク初期化
 *
 *	必ずMushitoriSys_End()で解放すること！
 */
FLD_MUSHITORI_WORK* MushitoriSys_Init(FIELDSYS_WORK* fsys,u16 week)
{
	FLD_MUSHITORI_WORK* wk = sys_AllocMemory(HEAPID_BASE_APP,sizeof(FLD_MUSHITORI_WORK));

	MI_CpuClear8(wk,sizeof(FLD_MUSHITORI_WORK));
	wk->heapID = HEAPID_BASE_APP;
	wk->save = fsys->savedata;

	//ゲーム用パラメータ初期化
	wk->ball_num = MUSHITORI_BALL_NUM;
	wk->fld_pp = PokemonParam_AllocWork(wk->heapID);

	//全国図鑑か？
	wk->zukan_f = ZukanWork_GetZenkokuZukanFlag(SaveData_GetZukanWork(wk->save));

	//イベントに突入した瞬間の曜日をスクリプトから引き継ぐ
	wk->week = week;

	//手持ちPush
	sys_PokePartyPush(wk);

	//トレーナー抽選
	sys_EntryCheck(wk);

	//エンカウントデータテーブル取得
	sys_GetEncountTbl(wk);

	//強制アップデート
	EVTIME_Update(fsys);

	return wk;
}

/**
 *	@brief	虫取り大会ワーク解放
 */
void MushitoriSys_End(FLD_MUSHITORI_WORK* wk)
{
	//手持ち書き戻し&捕まえたポケモン追加
	sys_PokePartyPop(wk);

	//ワークメモリ解放
	sys_FreeMemoryEz(wk->fld_pp);

	sys_FreeMemoryEz(wk);
}

/**
 *	@brief	虫取り大会評価
 */
void MushitoriSys_Rating(FLD_MUSHITORI_WORK* wk)
{
	int i,j,hiest,hikey,tmp;
	MUSHITORI_ENTRY* mp;

	//自分が捕まえたポケモンのデータを格納
	mp = &wk->entry[MY_ENTRY_ID];
	mp->id = MY_ENTRY_ID;
	mp->point = sys_CalcPoint(wk,wk->fld_pp);
	mp->poke.monsno = PokeParaGet(wk->fld_pp,ID_PARA_monsno,NULL);

	for(i = 0;i < MUSHITORI_ENTRY_MAX;i++){
		wk->ranking[i] = i;
	}
	//順位を決定
	for(i = 0;i < (MUSHITORI_ENTRY_MAX-1);i++){
		hiest = i;
		hikey = wk->entry[wk->ranking[i]].point;
		for(j = i+1;j<MUSHITORI_ENTRY_MAX;j++){
			if(wk->entry[wk->ranking[j]].point >= hikey){
				hiest = j;
				hikey = wk->entry[wk->ranking[j]].point;
			}
		}
		tmp = wk->ranking[i];
		wk->ranking[i] = wk->ranking[hiest];
		wk->ranking[hiest] = tmp;
	}
	//自分の順位と賞品を決定
	wk->my_rank = 3;
	for(i = 0;i < 3;i++){
		if(wk->ranking[i] == MY_ENTRY_ID){
			wk->my_rank = i;
			break;
		}
	}
	switch(wk->my_rank){
	case 0:
		{
			u8 rnd;
			if(wk->zukan_f){
				rnd = gf_rand()%10;
			}else{
				rnd = 0;
			}
			if(rnd < 6){
				wk->item = ITEM_TAIYOUNOISI+rnd;
			}else{
				wk->item = ITEM_HIKARINOISI+(rnd-6);
			}
		}
		break;
	case 1:
		wk->item = ITEM_KAWARAZUNOISI;
		break;
	case 2:
		wk->item = ITEM_OBONNOMI;
		break;
	default:
		wk->item = ITEM_KIREINANUKEGARA;
		break;
	}
}

/**
 *	@brief	指定順位のデータをバッファ展開
 */
void MushitoriSys_RankingDataBufSet(FLD_MUSHITORI_WORK* wk,const MSGDATA_MANAGER * msgman,WORDSET* wset,u8 rank)
{
	MUSHITORI_ENTRY* mp = &wk->entry[wk->ranking[rank]];
	static const u16 tr_type[] = {
		TRTYPE_MUSHI,TRTYPE_MUSHI,TRTYPE_ELITEW,TRTYPE_DAISUKIM,TRTYPE_MUSHI,
		TRTYPE_CAMPB,TRTYPE_PICNICG,TRTYPE_MUSHI,TRTYPE_TANPAN,TRTYPE_SCHOOLB,
	};

	if(rank == wk->my_rank){	//自分
		WORDSET_RegisterPlayerName( wset, 0, SaveData_GetMyStatus(wk->save) );
	}else{
		STRBUF* name = 	MSGMAN_AllocString( msgman, msg_mushi_player_name_0+mp->id );
		WORDSET_RegisterWord( wset, 0, name, PM_NEUTRAL, TRUE, PM_LANG);
		STRBUF_Delete(name);
		WORDSET_RegisterTrTypeName( wset, 2, tr_type[mp->id]);
	}
	//ポケモン名
	WORDSET_RegisterMonsName( wset,1, mp->poke.monsno);
	//得点
	WORDSET_RegisterNumber( wset, 3, mp->point, 3, NUMBER_DISPTYPE_LEFT, NUMBER_CODETYPE_DEFAULT );
}

/**
 *	@brief	指定したナンバーのトレーナーがエントリーしているか？
 */
BOOL MushitoriSys_TrainerEntryCheck(FLD_MUSHITORI_WORK* wk,u8 entry_id)
{
	int i;

	for(i = 0;i < MUSHITORI_ENEMY_MAX;i++){
		if(wk->entry[i].id == entry_id){
			return TRUE;
		}
	}
	return FALSE;
}

/**
 *	@brief	捕まえたポケモンを手持ちorボックスどちらに追加かチェック&捕まえたポケモンのニックネーム展開
 *
 *	@retval	TRUE:ボックス転送した
 *
 *	@li	実際のポケモン追加処理はPokeParaPopで行う
 */
BOOL MushitoriSys_CapturePokePush(FLD_MUSHITORI_WORK* wk,WORDSET* wset,u8 buf_id)
{
	STRBUF* msg_buf;

	if(!wk->get_f){
		return FALSE;
	}
	msg_buf = STRBUF_Create(BUFLEN_POKEMON_NAME,wk->heapID);
	//この後のタイミングで使うので、ついでにニックネームをバッファ展開
	PokeParaGet(wk->fld_pp, ID_PARA_nickname_buf, msg_buf);
	WORDSET_RegisterWord( wset, buf_id, msg_buf, PM_NEUTRAL, TRUE, PM_LANG);
	STRBUF_Delete(msg_buf);

	if(wk->temoti_num >= 6){
		return TRUE;
	}
	return FALSE;
}

/*
 *	@brief	虫取り大会のエンカウントテーブルをアロケーションして取得する
 *
 *	＊呼び出し側が利用後に解放すること！
 */
ENC_COMMON_DATA* MushitoriSys_AllocEncountTbl( FLD_MUSHITORI_WORK* wk,int heapID)
{
	int i;
	u16 rnd,idx = 0;
	ENC_COMMON_DATA* data;

	data = sys_AllocMemoryLo(heapID,sizeof(ENC_COMMON_DATA));

	rnd = gf_rand()%100;
	for(i = 0;i < MUSHITORI_ENC_POKE_NUM;i++){
		if(rnd >= wk->enc_tbl[i].enc_key){
			idx = i;
			break;
		}
	}
	data->MonsNo = wk->enc_tbl[i].monsno;
	{
		u8 lv_div = (wk->enc_tbl[i].lvMax-wk->enc_tbl[i].lvMin)+1;
		data->LvMax = wk->enc_tbl[i].lvMin+gf_rand()%lv_div;
		data->LvMin = 0;
	}
	return data;
}

/**
 *	@brief	ポケパーティPush(手持ちを戦闘可能な一匹のみにする)
 */
static void sys_PokePartyPush(FLD_MUSHITORI_WORK* wk)
{
	int i;

	//退避用領域作成
	wk->party_s = PokeParty_AllocPartyWork(wk->heapID);
	//手持ち取得
	wk->party_t = SaveData_GetTemotiPokemon(wk->save);
	//退避
	PokeParty_Copy(wk->party_t,wk->party_s);

	wk->temoti_num = PokeParty_GetPokeCount(wk->party_t);
	wk->entry_poke = GetFrontPokemon2(wk->save);
	//戦闘可能な最初の一匹以外手持ちから削除
	for(i = 0;i < wk->temoti_num-1;i++){
		if(i < wk->entry_poke){
			PokeParty_Delete(wk->party_t, 0);
		}else{
			PokeParty_Delete(wk->party_t, 1);
		}
	}
	OS_Printf("temoti_num = %d\n",PokeParty_GetPokeCount(wk->party_t));
}

/**
 *	@brief	ポケパーティ　復帰させる
 */
static void sys_PokePartyPop(FLD_MUSHITORI_WORK* wk)
{
	POKEMON_PARAM* pp;
	PM_CONDITION pc;

	//参加ポケモンのパラメータをいったん退避
	pp = PokemonParam_AllocWork(wk->heapID);
	PokeCopyPPtoPP(PokeParty_GetMemberPointer(wk->party_t,0),pp);
	PokeParty_GetPokeCondition(wk->party_t,&pc,0);

	//スタックを書き戻し
	PokeParty_Copy(wk->party_s,wk->party_t);

	//参加ポケモンパラメータを元の場所に書き戻し
	PokeParty_SetMemberData( wk->party_t, wk->entry_poke, pp);
	PokeParty_SetPokeCondition(wk->party_t,&pc,wk->entry_poke);

	sys_FreeMemoryEz(pp);
	sys_FreeMemoryEz(wk->party_s);
	wk->party_s = NULL;

	if(!wk->get_f){
		return;
	}
	//捕まえたポケモンを追加
	if(wk->temoti_num >= 6){
		//ボックス転送
		BOXDAT_PutPokemon( SaveData_GetBoxData(wk->save), PPPPointerGet(wk->fld_pp));
	}else{
		//手持ちに追加する
		PokeParty_Add(wk->party_t, wk->fld_pp);
	}
}

/*
 *	@brief	まだ未抽選のトレーナーかチェック
 *
 *	すでに登録済みならTRUE
 */
static BOOL sys_TrainerIsAlreadyEntry(FLD_MUSHITORI_WORK* wk,u8 id,u8 num)
{
	int i;
	for(i = 0;i < num;i++){
		if(wk->entry[i].id == id){
			return TRUE;
		}
	}
	return FALSE;
}
/**
 *	@brief	エントリー抽選を行う
 */
static void sys_EntryCheck(FLD_MUSHITORI_WORK* wk)
{
	int i,j,size,num;
	FSFile	fp;
	MUSHITORI_POKE* ptbl,*tp;
	u8*	entry;

	FS_InitFile(&fp);
	if(!FS_OpenFile(&fp,MUSHI_TRAINER_DAT_FILE)){
		GF_ASSERT_MSG(0,"FS_OpenFile(\"%s\")...Failed\n",MUSHI_TRAINER_DAT_FILE);
		return;
	}
	//ファイル長取得
	size = FS_GetLength(&fp);

	//アロケーションメモリ確保
	ptbl = sys_AllocMemoryLo(wk->heapID,size);
	entry = sys_AllocMemoryLo(wk->heapID,sizeof(u8)*MUSHITORI_TRAINER_POKE_MAX);

	//データ配列数取得
	FS_ReadFile(&fp,ptbl,size);

	//配列初期化
	for(i = 0;i < MUSHITORI_ENEMY_MAX;i++){
		wk->entry[i].id = 0xFF;
	}

	//ID抽選
	for(i = 0;i < MUSHITORI_ENEMY_MAX;i++){
		s16 revision;
		u8 id;

		do{
			id = gf_rand()%MUSHITORI_TRAINER_MAX;
		}while(	sys_TrainerIsAlreadyEntry(wk,id,i));
		wk->entry[i].id = id;

		//エントリー可能なポケモン抽選
		num = 0;
		tp = &ptbl[wk->entry[i].id*MUSHITORI_TRAINER_POKE_MAX];

		for(j = 0;j < MUSHITORI_TRAINER_POKE_MAX;j++){
			if(tp[j].type && !wk->zukan_f){
				continue;
			}
			if(tp[j].week < 7 && tp[j].week != wk->week){
				continue;
			}
			entry[num++] = j;
		}
		MI_CpuCopy8(&tp[entry[gf_rand()%num]],&wk->entry[i].poke,sizeof(MUSHITORI_POKE));

		revision = gf_rand()%(wk->entry[i].poke.revision*2)-(wk->entry[i].poke.revision);
		wk->entry[i].point = wk->entry[i].poke.point+revision;
	}

	sys_FreeMemoryEz(entry);
	sys_FreeMemoryEz(ptbl);

	(void)FS_CloseFile(&fp);
}

/*
 *	@brief	虫取り大会エンカウントテーブルを取得する
 */
static void sys_GetEncountTbl(FLD_MUSHITORI_WORK* wk)
{
	int i,j,size,idx;
	FSFile	fp;
	MUSHITORI_ENC_POKE* ptbl,*tp;
	u8*	entry;

	FS_InitFile(&fp);
	if(!FS_OpenFile(&fp,MUSHI_ENCOUNT_DAT_FILE)){
		GF_ASSERT_MSG(0,"FS_OpenFile(\"%s\")...Failed\n",MUSHI_ENCOUNT_DAT_FILE);
		return;
	}
	//ファイル長取得
	size = FS_GetLength(&fp);

	//アロケーションメモリ確保
	ptbl = sys_AllocMemoryLo(wk->heapID,size);

	//データ配列数取得
	FS_ReadFile(&fp,ptbl,size);

	if(wk->zukan_f){
		idx = wk->week / 2;
	}else{
		idx = 0;
	}

	MI_CpuCopy8(&ptbl[MUSHITORI_ENC_POKE_NUM*idx],wk->enc_tbl,sizeof(MUSHITORI_ENC_POKE)*MUSHITORI_ENC_POKE_NUM);
	sys_FreeMemoryEz(ptbl);

	(void)FS_CloseFile(&fp);
}

/**
 *	@brief	捕まえたポケモンの得点計算を行う
 */
static u16 sys_CalcPoint(FLD_MUSHITORI_WORK* wk,POKEMON_PARAM* pp)
{
	int i;
	u16 point = 0,value,monsno;
	MUSHITORI_ENC_POKE* ep = NULL;
	if(!wk->get_f){
		return 0;
	}
	//レア係数
	monsno  = PokeParaGet(pp,ID_PARA_monsno,NULL);
	for(i = 0;i < MUSHITORI_ENC_POKE_NUM;i++){
		if(wk->enc_tbl[i].monsno == monsno){
			ep = &wk->enc_tbl[i];
			point += wk->enc_tbl[i].rare;
			break;
		}
	}
	if(ep == NULL){
		GF_ASSERT_MSG(0,"不正MonsNo %d\n",monsno);
		return 0;
	}
	//レベル
	point += (100*PokeParaGet(pp,ID_PARA_level,NULL)/ep->lvMax);

	//乱数
	value = 0;
	for(i = 0;i < 6;i++){
		value += PokeParaGet(pp,ID_PARA_hp_rnd+i,NULL);
	}
	point += (100*value/186);

	//HP
	point += (100*PokeParaGet(pp,ID_PARA_hp,NULL)/PokeParaGet(pp,ID_PARA_hpmax,NULL));

	OS_Printf("MushitoriPoint = %d\n",point);
	return point;
}